package com.wenx.v3system.modular.cloud.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.wenx.v3core.error.BusinessException;
import com.wenx.v3system.modular.oauth.prop.OAuth2ClientProp;
import com.wenx.v3system.modular.cloud.domain.dto.UserAuthResponseDto;
import com.wenx.v3system.modular.cloud.domain.dto.UserLoginDto;
import com.wenx.v3system.modular.cloud.domain.dto.UserRegisterDto;
import com.wenx.v3system.modular.cloud.domain.po.SysUser;
import com.wenx.v3system.modular.cloud.mapper.SysUserMapper;
import com.wenx.v3system.modular.cloud.service.SysAuthService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.*;
import org.springframework.scheduling.annotation.Async;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import java.time.LocalDateTime;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 系统认证服务实现
 * 负责用户注册、登录、登出等认证相关功能
 *
 * @author wenx
 * @since 1.0.0
 */
@Slf4j
@Service
public class SysAuthServiceImpl implements SysAuthService {

    private final SysUserMapper userMapper;
    private final PasswordEncoder passwordEncoder;
    private final OAuth2ClientProp oauth2ClientProp;
    private final RestTemplate loadBalancedRestTemplate;
    private final JwtDecoder jwtDecoder;

    public SysAuthServiceImpl(
            SysUserMapper userMapper,
            PasswordEncoder passwordEncoder,
            OAuth2ClientProp oauth2ClientProp,
            @Qualifier("oauth2LoadBalancedRestTemplate") RestTemplate loadBalancedRestTemplate,
            JwtDecoder jwtDecoder) {
        this.userMapper = userMapper;
        this.passwordEncoder = passwordEncoder;
        this.oauth2ClientProp = oauth2ClientProp;
        this.loadBalancedRestTemplate = loadBalancedRestTemplate;
        this.jwtDecoder = jwtDecoder;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public UserAuthResponseDto register(UserRegisterDto registerDto) {
        log.info("用户注册: username={}", registerDto.getUsername());

        // 校验用户信息是否已存在
        validateUserNotExists(registerDto);

        // 创建用户实体
        SysUser user = createUserFromRegisterDto(registerDto);

        // 保存用户
        int result = userMapper.insert(user);
        if (result <= 0) {
            throw new BusinessException("用户注册失败");
        }

        log.info("用户注册成功: userId={}, username={}", user.getId(), user.getUsername());

        // 调用auth服务进行认证并生成token
        Map<String, Object> authResult = authenticateWithAuthService(user.getUsername(), registerDto.getPassword());
        
        // 从认证结果构建响应，并补充用户信息
        UserAuthResponseDto response = buildAuthResponseFromAuthResult(authResult);
        
        // 确保返回完整的用户信息（因为是刚注册的用户）
        response.setUserId(user.getId());
        response.setUsername(user.getUsername());
        response.setEmail(user.getEmail());
        response.setPhone(user.getPhone());
        response.setAvatar(user.getAvatar());
        response.setGender(user.getGender());
        response.setAge(user.getAge());
        response.setStatus(user.getStatus());
        response.setDepartmentId(user.getDepartmentId());
        response.setDescription(user.getDescription());
        
        return response;
    }

    @Override
    public UserAuthResponseDto login(UserLoginDto loginDto) {
        log.info("用户登录: username={}", loginDto.getUsername());

        // 直接调用auth服务进行验证并生成token
        Map<String, Object> authResult = authenticateWithAuthService(loginDto.getUsername(), loginDto.getPassword());
        
        // 从认证结果中获取用户信息并构建响应
        return buildAuthResponseFromAuthResult(authResult);
    }

    @Override
    public void logout(Long userId) {
        log.info("用户登出: userId={}", userId);

        // 查询用户信息
        SysUser user = userMapper.selectById(userId);
        if (user == null) {
            log.warn("登出时用户不存在: userId={}", userId);
            return;
        }

        // 调用auth服务撤销token
        String revokeUrl = oauth2ClientProp.getRevokeTokenUrl();

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        headers.setBasicAuth(oauth2ClientProp.getClientId(), oauth2ClientProp.getClientSecret());

        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("token_type_hint", "access_token");
        params.add("client_id", oauth2ClientProp.getClientId());

        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(params, headers);

        try {
            loadBalancedRestTemplate.postForEntity(revokeUrl, request, String.class);
            log.info("成功撤销用户token: userId={}", userId);
        } catch (Exception e) {
            log.warn("撤销token失败，但继续登出流程: userId={}, error={}", userId, e.getMessage());
        }

        log.info("用户登出成功: userId={}", userId);
    }

    @Override
    public UserAuthResponseDto refreshToken(String refreshToken) {
        log.info("刷新令牌: refreshToken={}", refreshToken);

        String tokenUrl = oauth2ClientProp.getTokenUrl();

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        headers.setBasicAuth(oauth2ClientProp.getClientId(), oauth2ClientProp.getClientSecret());

        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("grant_type", "refresh_token");
        params.add("refresh_token", refreshToken);

        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(params, headers);

        ResponseEntity<Map> response = loadBalancedRestTemplate.postForEntity(tokenUrl, request, Map.class);

        if (response.getStatusCode() != HttpStatus.OK || response.getBody() == null) {
            throw new BusinessException("刷新令牌失败");
        }

        Map<String, Object> tokenData = response.getBody();

        // 从新token中获取用户信息
        String newAccessToken = (String) tokenData.get("access_token");
        Jwt jwt = jwtDecoder.decode(newAccessToken);
        String username = processUsername(jwt.getClaimAsString("sub"));

        // 查询用户信息
        SysUser user = getActiveUserByUsername(username);
        if (user == null) {
            throw new BusinessException("用户不存在或已被禁用");
        }

        // 构建响应
        UserAuthResponseDto authResponse = buildUserResponse(user);
        authResponse.setAccessToken(newAccessToken);
        authResponse.setRefreshToken((String) tokenData.get("refresh_token"));
        authResponse.setExpiresIn(parseExpiresIn(tokenData.get("expires_in")));
        authResponse.setLoginTime(LocalDateTime.now());

        return authResponse;
    }

    @Override
    public Map<String, Object> validateToken(String token) {
        Map<String, Object> result = new HashMap<>();

        try {
            // 解码JWT token
            Jwt jwt = jwtDecoder.decode(token);
            String username = jwt.getClaimAsString("sub");

            if (!StringUtils.hasText(username)) {
                result.put("valid", false);
                result.put("message", "Token中缺少用户信息");
                return result;
            }

            // 处理用户名
            username = processUsername(username);

            // 查询用户信息
            SysUser user = getActiveUserByUsername(username);
            if (user == null) {
                result.put("valid", false);
                result.put("message", "用户不存在或已被禁用");
                return result;
            }

            // 验证成功
            result.put("valid", true);
            fillUserInfoToMap(result, user);

            return result;

        } catch (Exception e) {
            log.error("Token验证失败: {}", e.getMessage(), e);
            result.put("valid", false);
            result.put("message", "Token验证失败: " + e.getMessage());
            return result;
        }
    }

    @Override
    public UserAuthResponseDto getCurrentUser() {
        log.debug("获取当前用户信息");

        // 从Spring Security上下文获取当前用户
        org.springframework.security.core.Authentication authentication =
                org.springframework.security.core.context.SecurityContextHolder.getContext().getAuthentication();

        if (authentication == null || !authentication.isAuthenticated()) {
            throw new BusinessException("用户未登录");
        }

        String username = authentication.getName();
        if (!StringUtils.hasText(username)) {
            throw new BusinessException("无法获取用户信息");
        }

        // 处理用户名
        username = processUsername(username);

        // 查询用户信息
        SysUser user = getActiveUserByUsername(username);
        if (user == null) {
            throw new BusinessException("用户不存在或已被禁用");
        }

        // 构建响应
        return buildUserResponse(user);
    }

    @Override
    public boolean checkAccountExists(String account) {
        // account 和 username 相同，委托给 checkUsernameExists
        return checkUsernameExists(account);
    }

    @Override
    public boolean checkUsernameExists(String username) {
        if (!StringUtils.hasText(username)) {
            return false;
        }

        LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<SysUser>()
                .eq(SysUser::getUsername, username);

        return userMapper.selectCount(wrapper) > 0;
    }

    @Override
    public boolean checkEmailExists(String email) {
        if (!StringUtils.hasText(email)) {
            return false;
        }

        LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<SysUser>()
                .eq(SysUser::getEmail, email);

        return userMapper.selectCount(wrapper) > 0;
    }

    @Override
    public boolean checkPhoneExists(String phone) {
        if (!StringUtils.hasText(phone)) {
            return false;
        }

        LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<SysUser>()
                .eq(SysUser::getPhone, phone);

        return userMapper.selectCount(wrapper) > 0;
    }

    /**
     * 验证用户信息不存在
     */
    private void validateUserNotExists(UserRegisterDto registerDto) {


        if (checkUsernameExists(registerDto.getUsername())) {
            throw new BusinessException("用户名已存在");
        }

        if (StringUtils.hasText(registerDto.getEmail()) && checkEmailExists(registerDto.getEmail())) {
            throw new BusinessException("邮箱已存在");
        }

        if (StringUtils.hasText(registerDto.getPhone()) && checkPhoneExists(registerDto.getPhone())) {
            throw new BusinessException("手机号已存在");
        }
    }

    /**
     * 从注册DTO创建用户实体
     */
    private SysUser createUserFromRegisterDto(UserRegisterDto registerDto) {
        SysUser user = new SysUser();

        user.setUsername(registerDto.getUsername());
        user.setPassword(passwordEncoder.encode(registerDto.getPassword()));
        user.setEmail(registerDto.getEmail());
        user.setPhone(registerDto.getPhone());
        // UserRegisterDto 没有 realName 字段，暂时设置为 username
        user.setRealName(registerDto.getUsername());
        user.setGender(registerDto.getGender());
        user.setAge(registerDto.getAge());
        user.setDescription(registerDto.getDescription());
        user.setStatus(1); // 默认启用
        // BaseEntity 的时间字段会自动处理

        return user;
    }

    /**
     * 根据用户名查询用户
     */
    private SysUser getUserByUsername(String username) {
        LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<SysUser>()
                .eq(SysUser::getUsername, username);

        return userMapper.selectOne(wrapper);
    }

    /**
     * 异步更新最后登录时间
     * 使用异步调用避免阻塞主要的登录流程，提高响应性能
     */
    @Async
    void updateLastLoginTime(Long userId) {
        if (userId == null) {
            log.warn("用户ID为空，无法更新登录时间");
            return;
        }

        log.debug("异步更新用户最后登录时间: userId={}", userId);

        // 先查询当前用户信息以获取登录次数
        SysUser currentUser = userMapper.selectById(userId);
        if (currentUser == null) {
            log.warn("用户不存在，无法更新登录时间: userId={}", userId);
            return;
        }

        // 构建更新对象
        SysUser updateUser = new SysUser();
        updateUser.setId(userId);
        updateUser.setLastLoginTime(new Date());

        // 更新登录次数
        Integer currentLoginCount = currentUser.getLoginCount();
        updateUser.setLoginCount(currentLoginCount == null ? 1 : currentLoginCount + 1);

        userMapper.updateById(updateUser);
    }



    /**
     * 调用auth服务进行用户认证
     */
    private Map<String, Object> authenticateWithAuthService(String username, String password) {
        // 第一步：使用client_credentials获取系统级访问令牌
        String systemAccessToken = getSystemAccessToken();
        
        // 第二步：使用系统令牌调用自定义认证端点为用户生成令牌
        Map<String, Object> authResult = generateUserToken(systemAccessToken, username, password);
        if (authResult == null || authResult.get("access_token") == null) {
            throw new BusinessException("用户认证失败");
        }

        return authResult;
    }
    
    /**
     * 从auth服务认证结果构建用户响应
     */
    private UserAuthResponseDto buildAuthResponseFromAuthResult(Map<String, Object> authResult) {
        UserAuthResponseDto response = new UserAuthResponseDto();
        
        // 设置token信息
        response.setAccessToken((String) authResult.get("access_token"));
        response.setRefreshToken((String) authResult.get("refresh_token"));
        response.setExpiresIn(parseExpiresIn(authResult.get("expires_in")));
        response.setLoginTime(LocalDateTime.now());
        
        // 从token中解析用户信息
        String accessToken = (String) authResult.get("access_token");
        if (accessToken != null) {
            try {
                Jwt jwt = jwtDecoder.decode(accessToken);
                String username = processUsername(jwt.getClaimAsString("sub"));
                
                // 查询用户信息补充到响应中
                SysUser user = getUserByUsername(username);
                if (user != null) {
                    response.setUserId(user.getId());
                    response.setUsername(user.getUsername());
                    response.setEmail(user.getEmail());
                    response.setPhone(user.getPhone());
                    response.setAvatar(user.getAvatar());
                    response.setGender(user.getGender());
                    response.setAge(user.getAge());
                    response.setStatus(user.getStatus());
                    response.setDepartmentId(user.getDepartmentId());
                    response.setDescription(user.getDescription());
                    
                    // 更新最后登录时间
                    updateLastLoginTime(user.getId());
                }
            } catch (Exception e) {
                log.warn("解析token中的用户信息失败: {}", e.getMessage());
            }
        }
        
        return response;
    }

    /**
     * 获取系统级访问令牌（使用client_credentials模式）
     */
    private String getSystemAccessToken() {
        String tokenUrl = oauth2ClientProp.getTokenUrl();
        
        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("grant_type", "client_credentials");
        params.add("client_id", oauth2ClientProp.getClientId());
        params.add("client_secret", oauth2ClientProp.getClientSecret());
        params.add("scope", "read write"); // 添加必要的scope
        
        Map<String, Object> tokenData = postFormForMap(tokenUrl, params);
        if (tokenData == null || tokenData.get("access_token") == null) {
            throw new BusinessException("获取系统访问令牌失败：未返回有效的access_token");
        }
        
        return (String) tokenData.get("access_token");
    }
    
    /**
     * 使用系统令牌为用户生成令牌
     * 参考platform auth逻辑，调用auth服务的validate接口验证用户并生成令牌
     */
    private Map<String, Object> generateUserToken(String systemToken, String username, String password) {
        String validateUrl = oauth2ClientProp.getBaseUrl() + "/api/auth/validate";
        
        HttpHeaders headers = new HttpHeaders();
        headers.setBearerAuth(systemToken);
        headers.setContentType(MediaType.APPLICATION_JSON);
        
        // 为系统用户添加前缀，确保CompositeUserDetailsService能正确路由
        String systemUsername = "sys:" + username;
        
        // 构建请求体 - validate接口需要用户名和密码
        Map<String, String> requestBody = Map.of(
            "username", systemUsername,
            "password", password
        );
        
        HttpEntity<Map<String, String>> request = new HttpEntity<>(requestBody, headers);
        
        try {
            ResponseEntity<Map> response = loadBalancedRestTemplate.postForEntity(validateUrl, request, Map.class);
            Map<String, Object> result = response.getBody();
            
            // 检查认证结果
            if (result == null || !Boolean.TRUE.equals(result.get("success"))) {
                String errorMsg = result != null ? (String) result.get("message") : "用户认证失败";
                throw new BusinessException("用户认证失败: " + errorMsg);
            }
            
            return result;
        } catch (Exception e) {
            log.error("调用auth服务验证用户失败: username={}, error={}", systemUsername, e.getMessage());
            throw new BusinessException("用户认证服务调用失败: " + e.getMessage());
        }
    }
    
    /**
     * 发送表单请求并返回Map结果
     */
    private Map<String, Object> postFormForMap(String url, MultiValueMap<String, String> params) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        
        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(params, headers);
        ResponseEntity<Map> response = loadBalancedRestTemplate.postForEntity(url, request, Map.class);
        
        return response.getBody();
    }

    /**
     * 解析过期时间
     */
    private Long parseExpiresIn(Object expiresIn) {
        if (expiresIn instanceof Number) {
            return ((Number) expiresIn).longValue();
        } else if (expiresIn instanceof String) {
            try {
                return Long.parseLong((String) expiresIn);
            } catch (NumberFormatException e) {
                log.warn("无法解析过期时间: {}", expiresIn);
            }
        }
        return 7200L; // 默认2小时
    }

    /**
     * 处理用户名，移除系统前缀
     */
    private String processUsername(String username) {
        if (username != null && username.startsWith("sys_")) {
            return username.substring(4);
        }
        return username;
    }

    /**
     * 根据用户名查询活跃用户
     */
    private SysUser getActiveUserByUsername(String username) {
        return userMapper.selectOne(
                new LambdaQueryWrapper<SysUser>()
                        .eq(SysUser::getUsername, username)
                        .eq(SysUser::getStatus, 1)
        );
    }

    /**
     * 构建用户响应DTO（不包含token信息）
     */
    private UserAuthResponseDto buildUserResponse(SysUser user) {
        UserAuthResponseDto response = new UserAuthResponseDto();
        response.setUserId(user.getId());
        response.setUsername(user.getUsername());
        response.setEmail(user.getEmail());
        response.setPhone(user.getPhone());
        response.setAvatar(user.getAvatar());
        response.setGender(user.getGender());
        response.setAge(user.getAge());
        response.setStatus(user.getStatus());
        response.setDepartmentId(user.getDepartmentId());
        response.setDescription(user.getDescription());
        return response;
    }

    /**
     * 填充用户信息到Map中
     */
    private void fillUserInfoToMap(Map<String, Object> result, SysUser user) {
        result.put("userId", user.getId());
        result.put("username", user.getUsername());
        result.put("email", user.getEmail());
        result.put("phone", user.getPhone());
        result.put("avatar", user.getAvatar());
        result.put("gender", user.getGender());
        result.put("age", user.getAge());
        result.put("status", user.getStatus());
        result.put("departmentId", user.getDepartmentId());
        result.put("description", user.getDescription());
    }
}